package com.lin.open.server.modules.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lin.open.core.exception.BusinessException;
import com.lin.open.core.response.ResponseCode;
import com.lin.open.core.utils.IdUtil;
import com.lin.open.core.utils.PasswordUtil;
import com.lin.open.cache.redis.service.AnnotationCacheService;
import com.lin.open.server.modules.user.constants.UserConstants;
import com.lin.open.server.modules.user.context.UserLoginContext;
import com.lin.open.server.modules.user.context.UserRegisterContext;
import com.lin.open.server.modules.user.converter.UserConverter;
import com.lin.open.server.modules.user.entity.User;
import com.lin.open.server.modules.user.service.UserService;
import com.lin.open.server.modules.user.mapper.UserMapper;
import com.lin.open.server.modules.user.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Date;
import java.util.Objects;

/**
 * @author STD7347
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2023-12-16 13:52:32
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Autowired
    @Qualifier(value = "userAnnotationCacheService")
    private AnnotationCacheService<User> cacheService;

    @Autowired
    private UserConverter userConverter;

    /**
     * 用户注册
     * 1、参数校验
     * 2、执行注册操作
     *
     * @param userRegisterContext
     * @return
     */
    @Override
    public void register(UserRegisterContext userRegisterContext) {
        checkRegister(userRegisterContext);
        assembleUserEntity(userRegisterContext);
        doRegister(userRegisterContext);
    }

    /**
     * 用户登录
     * 1、校验用户密码是否正确
     * 2、使用sa-token生成token，并自动保存到redis当中
     *
     * @param context
     * @return 返回带有时效性的token
     */
    @Override
    public String login(UserLoginContext context) {
        checkLogin(context);
        String token = generateAndSaveAccessToken(context);
        return token;
    }

    /**
     * 查询用户信息
     *
     * @return
     */
    @Override
    public UserInfoVO info() {
        Serializable id = (Serializable) StpUtil.getLoginIdByToken(StpUtil.getTokenValue());
        if(Objects.equals(id,null)){
            throw new BusinessException("当前用户未登录");
        }
        User entity = getById(id);

        if(Objects.equals(entity,null)) {
            throw new BusinessException("获取用户信息失败");
        }

        UserInfoVO userInfoVO = userConverter.User2UserInfoVO(entity);
        return userInfoVO;
    }


    /********************************************************私有方法********************************************************/

    /**
     * 生成token信息，并放入缓存
     * 且将用户信息放入到redis当中
     * @param context
     * @return
     */
    private String generateAndSaveAccessToken(UserLoginContext context) {
        User entity = context.getEntity();
        if(Objects.equals(entity,null)){
            throw new BusinessException("用户不存在！");
        }
        String tokenValue = StpUtil.getTokenValue();
        StpUtil.login(entity.getId());
        return tokenValue;
    }

    /**
     * 校验账号密码是否正确
     * 将密码加密之后 进行对比
     *
     * @param context
     */
    private void checkLogin(UserLoginContext context) {
        User entity = getUserInfoByAccount(context.getUserAccount());

        String password = PasswordUtil.encryptPassword(UserConstants.SALT, context.getUserPassword());

        if (Objects.isNull(entity)) {
            throw new BusinessException("用户不存在");
        }

        if (!Objects.equals(password, entity.getUserPassword())) {
            throw new BusinessException("用户账号密码不匹配!");
        }

        context.setEntity(entity);
    }

    /**
     * 根据用户账号获取用户信息
     *
     * @param userAccount
     * @return
     */
    private User getUserInfoByAccount(String userAccount) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserAccount, userAccount);

        return getOne(queryWrapper);
    }

    /**
     * 实体转化
     * 由上下文信息转化成用户实体，封装进上下文
     *
     * @param userRegisterContext
     */
    private void assembleUserEntity(UserRegisterContext userRegisterContext) {
        String password = PasswordUtil.encryptPassword(UserConstants.SALT, userRegisterContext.getUserPassword());

        User entity = new User();
        entity.setId(IdUtil.get());
        entity.setUserAccount(userRegisterContext.getUserAccount());
        entity.setUserPassword(password);
        entity.setUpdateTime(new Date());
        entity.setCreateTime(new Date());

        userRegisterContext.setEntity(entity);
    }

    /**
     * 执行注册操作
     * 用户名全局唯一
     * 解决方案：
     * 幂等性通过数据库表对于用户名字段添加唯一索引，捕获到数据库插入异常之后，那么就是用户名重复
     */
    private void doRegister(UserRegisterContext userRegisterContext) {
        User entity = userRegisterContext.getEntity();

        if (Objects.nonNull(entity)) {
            try {
                synchronized (entity.getId()) {
                    if (!save(entity)) {
                        throw new BusinessException("用户注册失败！");
                    }
                }
            } catch (Exception e) {
                log.info("用户注册失败：" + e.getMessage());
                throw new BusinessException("用户已存在！");
            }
            return;
        }
        throw new BusinessException(ResponseCode.ERROR_PARAM);
    }

    /**
     * 用户注册参数校验
     *
     * @param userRegisterContext
     */
    private void checkRegister(UserRegisterContext userRegisterContext) {
        if (StringUtils.length(userRegisterContext.getUserAccount()) < 4) {
            throw new BusinessException("账号长度过段!");
        }

        if (StringUtils.length(userRegisterContext.getUserPassword()) < 4) {
            throw new BusinessException("用户密码太短!");
        }
    }

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    @Override
    public User getById(Serializable id) {
        return cacheService.getById(id);
    }
}




